import fs from 'fs'
import { PrismaClient as PgClient } from '../../../prisma/generated/uctoo'
require('dotenv').config()
const {  DATABASE_URL } = process.env
const db = new PgClient({
  log: ['query', 'info', 'warn', 'error'],
  datasourceUrl: DATABASE_URL,
});

async function main() {
  const args = process.argv.slice(2); // 去掉数组中的前两个元素
  let dbName = args[0] ? args[0] : 'uctoo';
  const needLangs = args[1] ? args[1] : 'false' // 增加追加语言包
  // let path = process.cwd()
  let path = `${process.env.ADMIN_DIRECTORY}/src`
  path = path.replace('/uctoo-backend', '/uctoo-app-client-pc/apps/uctoo-app-client-pc/src')

  // 从db_info取出所有表
  try {
    // 查询是否存在数据库
    const result = await db.db_info.findMany({
      where: { is_table_hidden: 'NO', table_catalog: dbName },
      distinct: ['table_name'],
      orderBy: { table_name: "asc" }
    })
    console.log("result:", result.length)
    if (result.length === 0) {
      // 不存在，获取数据库信息失败
      console.log(`select db_info fail.`)
    } else {
      for (const row of result) {
        let tableName = row.table_name
        // 首字母小写
        let tableNameLower = convertToCamelCase(tableName)
        // 首字母大写
        const tableNameUpper = `${tableNameLower.charAt(0).toLocaleUpperCase()}${tableNameLower.slice(1)}`
        //  取出表结构fields
        const fields = await db.db_info.findMany({
          where: { table_name: tableName, is_column_hidden: 'NO', table_catalog: dbName },
          orderBy: { ordinal_position: 'asc' }
        })
        console.log(tableName, ",fields:", fields.length)

        if (fields.length > 0) {
          const targetPath = `${path}/views/${tableName}`
          createTypes(fields, tableNameUpper, tableNameLower, targetPath)
          const modelTplFile = `${path}/store/models/entity.ts.tpl`
          const modelTargetPath = `${path}/store/models`
          const storeIndexPath = `${path}/store/index.ts`
          createModel(fields, tableName, tableNameUpper, tableNameLower, modelTplFile, modelTargetPath, storeIndexPath)
          const formTplFile = `${path}/views/entity/editform.vue.tpl`
          createForm(fields, tableName, tableNameUpper, tableNameLower, formTplFile, targetPath)
          const apiTplFile = `${path}/views/entity/api.ts.tpl`
          createApi(tableName, tableNameUpper, tableNameLower, apiTplFile, targetPath)
          const indexTplFile = `${path}/views/entity/index.vue.tpl`
          createIndex(fields, tableName, tableNameUpper, tableNameLower, indexTplFile, targetPath)
          if (needLangs == "true") {
            const langsTargetPath = `${path}/locales/langs`
            appendLangs(fields, tableName, langsTargetPath)
          }
        }
      }
    }
  } catch (ex) {
    console.error(ex)
  }

}
function convertToCamelCase(str) {
  return str.toLowerCase().replace(/_(.)/g, function (match, group1) {
    return group1.toUpperCase();
  });
}
function typeConversion(type) {
  switch (type) {
    case "integer": return "number";
    case "Int": return "number";
    case "double precision": return "number";
    case "character varying": return "string";
    case "String": return "string";
    case "timestamp with time zone": return "Date";
    case "DateTime": return "Date";
    default: return "string"
  }
}
function typeDefaultValue(type) {
  // return "\"\""
  switch (type) {
    case "integer": return 0;
    case "Int": return 0;
    case "double precision": return 0;
    case "character varying": return "''";
    case "String": return "''";
    case "timestamp with time zone": return "new Date()";
    case "DateTime": return "new Date()";
    default: return "''"
  }
}
function typePiniaModelType(type) {
  switch (type) {
    case "uuid": return "string";
    case "integer": return "number";
    case "Int": return "number";
    case "double precision": return "number";
    case "text": return "string";
    case "character varying": return "string";
    case "String": return "string";
    case "timestamp with time zone": return "string";
    case "DateTime": return "string";
    default: return "string"
  }
}
function typePiniaModelDecorators(type) {
  switch (type) {
    case "uuid": return "@Uid";
    case "integer": return "@Num";
    case "Int": return "@Num";
    case "double precision": return "@Num";
    case "text": return "@Str";
    case "character varying": return "@Str";
    case "String": return "@Str";
    case "timestamp with time zone": return "@Attr";
    case "DateTime": return "@Attr";
    default: return "@Attr"
  }
}
function typeDefaultModelValue(type) {
  switch (type) {
    case "uuid": return "";
    case "integer": return 0;
    case "Int": return 0;
    case "double precision": return 0;
    case "character varying": return "''";
    case "String": return "''";
    case "timestamp with time zone": return "''";
    case "DateTime": return "''";
    default: return "''"
  }
}
function resetDefaultValue(type) {
  // return "\"\""
  switch (type) {
    case "integer": return 0;
    case "Int": return 0;
    case "double precision": return 0;
    case "character varying": return "''";
    case "String": return "''";
    case "timestamp with time zone": return null;
    case "DateTime": return null;
    default: return "''"
  }
}

// 生成types.ts文件
function createTypes(fields, tableNameUpper, tableNameLower, targetPath) {
  let context = `export interface ${tableNameUpper} {\r\n`
  context += `[property: string]: any;\r\n`
  for (const field of fields) {
    let str = '?';
    if (field.is_nullable === "NO") {
      str = '';
    }
    context += `${field.column_name}${str}:${typeConversion(field.data_type)};\r\n`
  }
  context += `}\r\n`
  context += `export interface ${tableNameUpper}Response {
  [property: string]: any;
  currentPage: number;
  ${tableNameLower}s: ${tableNameUpper}[];
  total: number;
}`

  // 判断是否有目录，没有要创建
  if (!fs.existsSync(targetPath)) {
    try {
      fs.mkdirSync(targetPath, { recursive: true });
    } catch (err) {
      console.error(err);
    }
  }
  try {
    if (context) {
      fs.writeFileSync(`${targetPath}/types.ts`, context)
      console.log(`writeFile ${targetPath}/types.ts success!`)
    } else {
      console.log(`writeFile ${targetPath}/types.ts error! no context`)
    }
  } catch (err) {
    console.error('处理文件出错', err);
  }
}
// 生成models.ts文件
async function createModel(fields, tableName, tableNameUpper, tableNameLower, tplFile, targetPath, indexPath) {
  const targetFile = `${targetPath}/${tableName}.ts`;
  
  // 判断是否有目录，没有要创建
  if (!fs.existsSync(targetPath)) {
    try {
      fs.mkdirSync(targetPath, { recursive: true });
    } catch (err) {
      console.error(err);
    }
  }
  
  try {
    // 目标文件保留区内容提取
    const preservedRegions = [];
    if (fs.existsSync(targetFile)) {
      const targetContent = fs.readFileSync(targetFile, 'utf-8');
      targetContent.replace(
        /\/\/#region Human-Code Preservation([\s\S]*?)\/\/#endregion Human-Code Preservation/g,
        (match, content) => {
          preservedRegions.push(content.trim());
          return match;
        }
      );
    }

    // 原有模板处理逻辑
    let context = fs.readFileSync(tplFile, 'utf-8');
    if (context) {
      // 模板预处理（添加占位符）
      let placeholderIndex = 0;
      context = context.replace(
        /\/\/#region Human-Code Preservation[\s\S]*?\/\/#endregion Human-Code Preservation/g,
        () => {
          const placeholder = `//#PRESERVE_${placeholderIndex++}#//`;
          return `//#region Human-Code Preservation\n${placeholder}\n//#endregion Human-Code Preservation`;
        }
      );

      // 处理pinia-orm模型的字段生成
      let modelFields = `static override entity = '${tableName}'\r\n\r\n`;
      for (const field of fields) {
        modelFields += `${typePiniaModelDecorators(field.data_type)}(${typeDefaultModelValue(field.data_type)}) declare ${field.column_name}: ${typePiniaModelType(field.data_type)} ${field.is_nullable === "NO" ? '' : '| null'}\r\n`;
      }

      context = context
        .replace(/Entity/g, tableNameUpper)
        .replace(/entity/g, tableNameLower)
        .replace(/tableName/g, tableName)
        .replace("REPLACE_MODELFIELDS", modelFields);

      // 恢复保留内容
      placeholderIndex = 0;
      context = context.replace(
        /\/\/#region Human-Code Preservation\n\/\/#PRESERVE_(\d+)#\/\/\n\/\/#endregion Human-Code Preservation/g,
        (match, index) => {
          const content = preservedRegions[Number(index)] || '';
          return `//#region Human-Code Preservation\n${content}\n//#endregion Human-Code Preservation`;
        }
      );  
    
      fs.writeFileSync(targetFile, context);
      
      //store/index.ts文件中添加导出语句
      const newPath = `./models/${tableName}`;
      appendUniqueExport(indexPath, newPath);
      console.log(`writeFile ${targetFile} success!`);
    }
  } catch (err) {
    console.error('处理文件出错', err);
  }
}

// 生成editform.vue文件
async function createForm(fields, tableName, tableNameUpper, tableNameLower, tplFile, targetPath) {
  const targetFile = `${targetPath}/editform.vue`
  // 判断是否有目录，没有要创建
  if (!fs.existsSync(targetPath)) {
    try {
      fs.mkdirSync(targetPath, { recursive: true });
    } catch (err) {
      console.error(err);
    }
  }
  try {
    if(tableName == "component_setting"){  //如果是组件配置表，直接返回，不生成表单，组件配置表单人工维护
      return  
    }
    // let tplData=''
    const data = fs.readFileSync(tplFile, 'utf-8')
    let context = data
    if (context) {
      let formState = `const ENTITY_PROXY: { [K in keyof ${tableNameUpper}]: null } = {\r\n`
      let rules = `const rules = {\r\n`
      let form = `<a-form v-if="isIdsEmpty" ref="formRef" :model="formState" :rules="rules">\r\n`
      let interfaceFormated = `interface Formated {\r\n      id?:string,\r\n`;
      let formatedFormState = `const formatedFormState: Formated = {\r\n      id: formState.id,\r\n`;
      let numberFields = `[`;

      for (const field of fields) {
        if (field.column_name === 'id' ||
          field.column_name === 'creator' ||
          field.column_name === 'created_at' ||
          field.column_name === 'updated_at' ||
          field.column_name === 'deleted_at') {
          formState += `${field.column_name}: null,\r\n`
          continue
        }
        formState += `${field.column_name}: null,\r\n`
        rules += ` ${field.column_name}: [{message: ${field.placeholder ? field.placeholder : `$t('page.Required')`}, required: ${field.is_nullable === "NO"}}],\r\n`;
        if (field.column_name === 'image' || field.column_name === 'images'|| field.column_name === 'picture' || field.column_name.endsWith('img') || field.column_name.endsWith('image') ||
        field.column_name.endsWith('_logo') || field.column_name.endsWith('avatar')) {     //图片类型字段
            form += `    <a-form-item v-if="selectedFormItems.includes('${field.column_name}')" :label="$t('database_column.${tableName}.${field.column_name}')" name="${field.column_name}">
            <ImageUpload v-model="formState.${field.column_name}" :max-count="20" :upload-text="$t('page.image_upload')" />
          </a-form-item>\r\n`
        }else{
          form += `    <a-form-item v-if="selectedFormItems.includes('${field.column_name}')" :label="$t('database_column.${tableName}.${field.column_name}')" name="${field.column_name}">
          <a-input v-model:value="formState.${field.column_name}" />
        </a-form-item>\r\n`
        }
        
        interfaceFormated += `      ${field.column_name}?: ${typeConversion(field.data_type)},\r\n`;;
        formatedFormState += `      ${field.column_name}: ${typeConversion(field.data_type) === 'number' ? `Number(formState.${field.column_name})` : `formState.${field.column_name}`},\r\n`;
        numberFields += `${typeConversion(field.data_type) === 'number' ? `'${field.column_name}',` : ``}`;

      }
      formState += `};\r\n `
      rules += `};\r\n`
      form += `</a-form>\r\n`
      interfaceFormated += `     };\r\n`;
      formatedFormState += `     };\r\n`;
      numberFields += `]`;

      context = context.replace(/Entity/g, tableNameUpper)
        .replace(/entity/g, tableNameLower)
        .replace(/tableName/g, tableName)
        .replace("REPLACE_FORM_STATE", formState)
        .replace("REPLACE_FORMATEDFORMSATE", formatedFormState)
        .replace("REPLACE_INTERFACEFORMATED", interfaceFormated)
        .replace("REPLACE_RULES", rules)
        .replace("REPLACE_FORM", form)
        .replace("REPLACE_NUMBER_FIELDS", numberFields)

        // 格式化后代码有bug，注释掉
   /*  try{
      const prettierOptions = {
        parser: 'vue', // 指定解析器为 Vue
        ...prettier.resolveConfig(targetFile), // 合并本地配置文件（如 .prettierrc）
        filepath: targetFile // 确保插件生效
      };
      context = await prettier.format(context, prettierOptions);
    } catch (formatError) {
      console.error('格式化失败，保留原始内容:', formatError);
    } */

      fs.writeFileSync(targetFile, context)
      console.log(`writeFile ${targetFile} success!`)
    } else {
      console.log(`writeFile ${targetFile} error! no context`)
    }
  } catch (err) {
    console.error('处理文件出错', err);
  }
}
// 生成api.ts文件
function createApi(tableName, tableNameUpper, tableNameLower, tplFile, targetPath) {
  const targetFile = `${targetPath}/api.ts`
  // 判断是否有目录，没有要创建
  if (!fs.existsSync(targetPath)) {
    try {
      fs.mkdirSync(targetPath, { recursive: true });
    } catch (err) {
      console.error(err);
    }
  }
  try {
    const data = fs.readFileSync(tplFile, 'utf-8')
    let context = data
    if (context) {
      context = context.replace(/Entity/g, tableNameUpper)
        .replace(/entity/g, tableNameLower)
        .replace(/tableName/g, tableName)
      fs.writeFileSync(targetFile, context)
      console.log(`writeFile ${targetFile} success!`)
    } else {
      console.log(`writeFile ${targetFile} error! no context`)
    }
  } catch (err) {
    console.error('处理文件出错', err);
  }
}
// 生成Index.vue文件
function createIndex(fields, tableName, tableNameUpper, tableNameLower, tplFile, targetPath) {
  const targetFile = `${targetPath}/index.vue`
  // 判断是否有目录，没有要创建
  if (!fs.existsSync(targetPath)) {
    try {
      fs.mkdirSync(targetPath, { recursive: true });
    } catch (err) {
      console.error(err);
    }
  }
  try {
    if(tableName == "component_setting"){  //如果是组件配置表，直接返回，不生成index，组件配置页人工维护
      return  
    }
    const data = fs.readFileSync(tplFile, 'utf-8')
    let context = data
    if (context) {
      let columns = `const columnsData = [\r\n`
      let searchForm = `<a-form layout="inline" class="mb-4 flex flex-wrap gap-4">\r\n`;
      let searchParams = ``;
      let searchState = `// 定义搜索表单的接口
interface SearchFormState {\r\n`;
      let searchConst = `// 修改 searchForm 的定义，添加类型
const searchForm = reactive<SearchFormState>({\r\n`;
      let searchOperatorState = `interface SearchOperatorState {\r\n`;
      let searchOperator = `const SearchOperator = reactive<SearchOperatorState>({\r\n`;
      let resetSearch = `const resetSearch = () => {\r\n`;
      let orderOptions = `const ORDEROPTIONS = [`;
      let fieldThird = 0;
      for (const field of fields) {
        fieldThird++;
        columns += `{ dataIndex: '${field.column_name}', key: '${field.column_name}', title: $t('database_column.${tableName}.${field.column_name}'), resizable: true },\r\n`
        if(fieldThird == 3){
          searchForm += `  <a-form-item :label=\"$t('database_column.${tableName}.${field.column_name}')\">
          <a-input
            v-model:value="searchForm.${field.column_name}"
            :placeholder=\"$t('database_column.${tableName}.${field.column_name}')\"
            allow-clear
          >
            <template #addonBefore>
              <a-select v-model:value="SearchOperator.${field.column_name}" :options="SearchOperatorOptions" style="width: 70px">
              </a-select>
            </template>
          </a-input>
        </a-form-item>\r\n
        <!-- 折叠面板，包含更多控件 -->
        <a-collapse v-model:activeKey="activeKey" :bordered="false" expandIconPosition="end" :ghost="true">
        <a-collapse-panel key="1" :header="isCollapsed ? $t('page.Expand'): $t('page.Collapse')" >
        `
        }else{
          if (field.column_name == 'created_at' || field.column_name == 'updated_at' || field.column_name == 'deleted_at') {
            searchForm += `  <a-form-item :label=\"$t('database_column.${tableName}.${field.column_name}')\">
            <a-select v-model:value="SearchOperator.${field.column_name}" :options="SearchOperatorOptions" style="width: 70px">
            </a-select>
            <a-date-picker v-model:value="searchForm.${field.column_name}" valueFormat="YYYY-MM-DDTHH:mm:ssZ" show-time :placeholder="$t('database_column.${tableName}.${field.column_name}')" >
            </a-date-picker>
          </a-form-item>\r\n`  
          
          } else {
            searchForm += `  <a-form-item :label=\"$t('database_column.${tableName}.${field.column_name}')\">
          <a-input
            v-model:value="searchForm.${field.column_name}"
            :placeholder=\"$t('database_column.${tableName}.${field.column_name}')\"
            allow-clear
          >
            <template #addonBefore>
              <a-select v-model:value="SearchOperator.${field.column_name}" :options="SearchOperatorOptions" style="width: 70px">
              </a-select>
            </template>
          </a-input>
        </a-form-item>\r\n`  
            
          }
          
        }
        
        if (field.column_name == 'created_at' || field.column_name == 'updated_at' || field.column_name == 'deleted_at') {
          searchParams += `   if (searchForm.${field.column_name}) params.${field.column_name} = searchForm.${field.column_name};\r\n`;
          searchState += `${field.column_name}: [string, string] | null;\r\n`;
          searchConst += `${field.column_name}: null,\r\n`
          searchOperatorState += `${field.column_name}: string | null;\r\n`;
          searchOperator += `${field.column_name}: null,\r\n`;
          resetSearch += `searchForm.${field.column_name} = null;\r\n`
        } else {

          searchParams += `   if (searchForm.${field.column_name}) params.${field.column_name} =  ${typeConversion(field.data_type) === 'number' ? `Number(searchForm.${field.column_name})` : `searchForm.${field.column_name}`} ;\r\n`;
          searchState += `${field.column_name}:${typeConversion(field.data_type)} | null;\r\n`
          searchConst += `${field.column_name}: null,\r\n`;
          searchOperatorState += `${field.column_name}: string | null;\r\n`;
          searchOperator += `${field.column_name}: null,\r\n`;
          resetSearch += `searchForm.${field.column_name}=null;\r\n`;
        }
        orderOptions += `'${field.column_name}',`;
      }
      columns += `{ dataIndex: 'action', key: 'action', fixed: 'right' as 'right', title:$t('operate')  },\r\n`
      columns += `];`
      searchForm += ` 
        </a-collapse-panel>
      </a-collapse>
      <div class="mutually-exclusive-select">
    <a-select
      v-model:value="selectedAscItems"
      mode="multiple"
      :placeholder="$t('page.OrderASC')"
      style="width: 200px; margin-right: 8px"
      :options="availableOptions"
    ></a-select>
    <a-select
      v-model:value="selectedDescItems"
      mode="multiple"
      :placeholder="$t('page.OrderDESC')"
      style="width: 200px"
      :options="availableOptions"
    ></a-select>
  </div>
        <a-form-item>
          <a-space>
            <a-button type="primary" @click="handleSearch">{{$t('page.Search')}}</a-button>
            <a-button @click="resetSearch">{{$t('page.resetSearch')}}</a-button>
          </a-space>
        </a-form-item>
      </a-form>\r\n`
      searchParams += `\r\n`;
      searchState += `}`;
      searchConst += `});\r\n`;
      searchOperatorState += `}`;
      searchOperator += `});\r\n`;
      resetSearch += `
  pagination.current = 1;
  searchStatus.value = false;
  queryParam.filter = null;
  fetchData();
};\r\n`;
      orderOptions += `];\r\n`;
      context = context.replace(/Entity/g, tableNameUpper)
        .replace(/entity/g, tableNameLower)
        .replace(/tableName/g, tableName)
        .replace(/REPLACE_COLUMNS/g, columns)
        .replace('REPLACE_SEARCH_PARAMS', searchParams)
        .replace('REPLACE_SEARCH_FORMSATE', searchState)
        .replace('REPLACE_SEARCH_FORMCONST', searchConst)
        .replace('REPLACE_SEARCH_FORM', searchForm)
        .replace('REPLACE_SEARCH_OPERATORSTATE', searchOperatorState)
        .replace('REPLACE_SEARCH_OPERATOR', searchOperator)
        .replace('REPLACE_RESETSEARTCH', resetSearch)
        .replace('REPLACE_ORDEROPTIONS', orderOptions)

      fs.writeFileSync(targetFile, context)
      console.log(`writeFile ${targetFile} success!`)
    } else {
      console.log(`writeFile ${targetFile} error! no context`)
    }
  } catch (err) {
    console.error('处理文件出错', err);
  }
}

// 生成en-US.json、zh-CN.json文件
function appendLangs(fields, tableName, targetPath) {
  let enColumns = `"${tableName}":{\r\n`
  let cnColumns = `"${tableName}":{\r\n`
  for (let index = 0; index < fields.length; index++) {
    const field = fields[index];
    enColumns += `      "${field.column_name}": "${field.column_name}"`
    if (field.column_name === 'id') {
      cnColumns += `      "${field.column_name}": "id"`
    } else {
      const columnArr = field.column_comment ? field.column_comment.split('。') : []
      cnColumns += `      "${field.column_name}": "${columnArr.length ? columnArr[0] :
        (field.column_comment ? field.column_comment : field.column_name)}"`
    }
    if (index < fields.length - 1) {
      cnColumns += `,`;
      enColumns += `,`
    }
    cnColumns += `\r\n`
    enColumns += `\r\n`
  }
  for (const field of fields) {

  }
  enColumns += `    },\r\n   "AUTO_APPEND": "AUTO_APPEND_CODE"`
  cnColumns += `    },\r\n   "AUTO_APPEND": "AUTO_APPEND_CODE"`
  const enTargetFile = `${targetPath}/en-US.json`
  const cnTargetFile = `${targetPath}/zh-CN.json`
  // 判断是否有目录，没有要创建
  if (!fs.existsSync(targetPath)) {
    try {
      fs.mkdirSync(targetPath, { recursive: true });
    } catch (err) {
      console.error(err);
    }
  }
  try {
    enColumns = enColumns.replace(" ", "")
    const enData = fs.readFileSync(enTargetFile, 'utf-8')
    let enContext = enData
    if (enContext) {
      enContext = enContext.replace("\"AUTO_APPEND\": \"AUTO_APPEND_CODE\"", enColumns)
      fs.writeFileSync(enTargetFile, enContext)
      console.log(`writeFile ${enTargetFile} success!`)
    } else {
      console.log(`writeFile ${enTargetFile} error! no context`)
    }

    cnColumns = cnColumns.replace(" ", "")
    const cnData = fs.readFileSync(cnTargetFile, 'utf-8')
    let cnContext = cnData
    if (cnContext) {
      cnContext = cnContext.replace("\"AUTO_APPEND\": \"AUTO_APPEND_CODE\"", cnColumns)
      fs.writeFileSync(cnTargetFile, cnContext)
      console.log(`writeFile ${cnTargetFile} success!`)
    } else {
      console.log(`writeFile ${cnTargetFile} error! no context`)
    }
  } catch (err) {
    console.error('处理文件出错', err);
  }
}
/**
 * 向指定文件追加新的导出语句，并确保内容唯一
 * @param filePath 目标文件路径（如 index.ts）
 * @param newExportPath 要添加的新导出路径（如 './models/uctoo_user'）
 */
function appendUniqueExport(filePath: string, newExportPath: string) {
  try {
    // 读取文件内容
    const content = fs.readFileSync(filePath, 'utf-8');
    const lines = content.split(/\r?\n/); // 兼容不同系统的换行符

    // 提取所有已存在的导出路径
    const exportPathRegex = /export \* from ['"](.*?)['"]/;
    const existingPaths = new Set<string>();
    
    for (const line of lines) {
      const match = line.match(exportPathRegex);
      if (match) existingPaths.add(match[1]);
    }

    // 判断路径是否存在并追加新行
    if (!existingPaths.has(newExportPath)) {
      const newLine = `export * from '${newExportPath}';`;
      lines.push(newLine);
      fs.writeFileSync(filePath, lines.join('\n'));
      console.log(`✅ 成功添加: ${newExportPath}`);
    } else {
      console.log(`⏩ 跳过重复: ${newExportPath}`);
    }
  } catch (error) {
    console.error('❌ 文件处理错误:', error);
  }
}
main()